﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using ATPlatforms.RabbitSkin.Serializers;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.MessagePatterns;

namespace ATPlatforms.RabbitSkin
{
    public class MessageConsumer<T> : BaseProducerConsumer
    {
        public ISerializeMessages<T> MessageSerializer { get; set; }

        public MessageConsumer(string server, string exchange, string exchangeType) :
            this(server, exchange, exchangeType, new BinaryMessageSerializer<T>())
        {
        }

        public MessageConsumer(string server, string exchange, string exchangeType, ISerializeMessages<T> messageSerializer) : 
            base(server, exchange, exchangeType)
        {
            MessageSerializer = messageSerializer;
        }

        //Callback for message received
        public delegate void onReceiveMessage(T message);
        public event onReceiveMessage onMessageReceived;

        //Create Exchange and then start consuming. A binding needs to be added before any messages will be delivered
        public override bool StartBasic()
        {
           if(base.StartBasic())
           {
               mQueue = Model.QueueDeclare();
               QueueWorkDelegate c = new QueueWorkDelegate(Consume);
               c.BeginInvoke(null, null);
               if (mExchangeType == ExchangeType.Fanout)
                   AddBinding("");//fanout has default binding
               return true;
           }
           return false;
        }

        //Add a binding between this consumers Queue and the Exchange with routingKey
        public void AddBinding(string routingKey)
        {
            Model.QueueBind(mQueue, Exchange, routingKey, false, null);
        }

        //Remove binding between this consumers Queue and the Exchange with routingKey
        public void RemoveBinding(string routingKey)
        {
            Model.QueueUnbind(mQueue, Exchange, routingKey, null);
        }


        private void Consume()
        {
            mSubscription = new Subscription(Model, mQueue, false);
            Running = true;
            while (Running)
            {
                BasicDeliverEventArgs basicDeliveryEventArgs = mSubscription.Next();
                try
                {
                    T messageContent = MessageSerializer.DeSerializeMessage(basicDeliveryEventArgs.Body);

                    if (onMessageReceived != null)
                        onMessageReceived.BeginInvoke(messageContent, null, null);
                }catch(SerializationException se)
                {
                    //do something or nothing? silent fail?
                }
                mSubscription.Ack(basicDeliveryEventArgs);

            }
        }

    }
}
